<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import axios from 'axios'
import { ElMessage } from 'element-plus'
import JSZip from 'jszip'
import FileSaver from 'file-saver'

// const baseUrl = 'http://localhost:8000'
const baseUrl = '/tts'

// 存储已经转换过后的声音列表
const mp3ResultList = ref([])
// 语音播放器组件引用
const audioRef = ref()

// 表单对象
const form = reactive({
  voiceList: [],
  localeDict: {},
  tts: {
    region: '',
    voice: '',
    rate: 0, // 语速
    pitch: 0, // 频率
    volume: 0, // 音调
    fm: '.mp3' // 音频格式
  },
  formatList: [
    {
      label: 'mp3',
      value: '.mp3'
    },
    {
      label: 'wav',
      value: '.wav'
    },
    {
      label: 'ogg',
      value: '.ogg'
    }
  ]
})

// 输入待转换的文本内容
const textInput = ref('')
// 转换按钮的提交加载状态
const submitLoading = ref(false)

const tmpVoiceOption = ref([])

// 语音播放
const playTTS = (url) => {
  audioRef.value.src = url
  audioRef.value.play()
}

// 语音转换
const onSubmit = () => {
  console.log('submit!')
  // 校验必须条件是否已经输入或选择
  if (
    textInput.value.length == 0 ||
    textInput.value.trim().length == 0 ||
    form.tts.voice.length == 0 ||
    form.tts.voice.trim().length == 0
  ) {
    ElMessage({
      message: '请检查输入或选项是否正确',
      type: 'error',
      plain: true
    })
    return
  }

  // 更新提交按钮状态为提交中
  submitLoading.value = true

  // 提交转换
  axios
    .post(baseUrl + '/tts', {
      voice: form.tts.voice,
      text: textInput.value,
      fm: form.tts.fm,
      rate: ((form.tts.rate || 0) >= 0 ? '+' : '') + (form.tts.rate || 0) + '%',
      pitch: ((form.tts.pitch || 0) >= 0 ? '+' : '') + (form.tts.pitch || 0) + 'Hz',
      volume: ((form.tts.volume || 0) >= 0 ? '+' : '') + (form.tts.volume || 0) + '%'
    })
    .then((res) => {
      console.log('submit...', res)
      submitLoading.value = false
      if (res.status == 200) {
        playTTS(baseUrl + '/static/' + res.data)
        mp3ResultList.value.push({
          text: textInput.value,
          src: baseUrl + '/static/' + res.data,
          id: mp3ResultList.value.length + 1
        })
      }
    })
    .catch((err) => {
      console.log('submit error...', err)
      submitLoading.value = false
    })
  // 保存当前配置到本地缓存
  localStorage.setItem('tts.conf', JSON.stringify(form.tts))
}

// 只回复操作可能没那么方便的选项
const onReset = () => {
  form.tts.rate = 0
  form.tts.pitch = 0
  form.tts.volume = 0
}

// 语音区域选择事件
const changeRegion = (value) => {
  console.log('changeRegion....', value)
  tmpVoiceOption.value = form.voiceList.filter((item) => item.name.startsWith(value))
}

// 音色选择事件
const changeVoice = (value) => {
  console.log('changeVoice...', value)
}

// 播放操作
const handlePlay = (index, row) => {
  playTTS(row.src)
}

// 下载操作
const handleDownload = (index, row) => {
  console.log('handleDownload...', index, row)
  const fm = row.src.substring(row.src.lastIndexOf('.'))
  axios({
    url: row.src,
    method: 'GET',
    responseType: 'blob'
  }).then((res) => {
    const blob = new Blob([res.data])
    const downloadElement = document.createElement('a')
    const href = window.URL.createObjectURL(blob) // 创建下载的链接
    downloadElement.href = href
    downloadElement.download = row.text + fm // 下载后文件名
    document.body.appendChild(downloadElement)
    downloadElement.click() // 点击下载
    document.body.removeChild(downloadElement) // 下载完成移除元素
    window.URL.revokeObjectURL(href) // 释放掉blob对象
  })
}

// 删除操作
const handleDel = (index, row) => {
  mp3ResultList.value.splice(index, 1)
}

// 打包下载：按序号为文件名打包成zip文件
const downloadAll = () => {
  console.log('downloadAll...')
  // 判断是否有mp3列表数据
  if (mp3ResultList.value.length == 0) {
    return
  }
  const zip = new JSZip()
  let promises = []
  mp3ResultList.value.forEach((item) => {
    let url = item.src
    const fm = item.src.substring(item.src.lastIndexOf('.'))
    let fileName = item.id + fm
    let promise = getUrlBuffer(url)
    promise.then((res) => {
      zip.file(fileName, res)
    })
    promises.push(promise)
  })
  console.log('下载文件长度：', promises.length)
  Promise.all(promises).then(() => {
    zip.generateAsync({ type: 'blob' }).then((content) => {
      // 生成当前时间格式化文本
      let date = new Date()
      let time =
        date.getFullYear() +
        '' +
        (date.getMonth() + 1) +
        '' +
        date.getDate() +
        '' +
        date.getHours() +
        '' +
        date.getMinutes() +
        '' +
        date.getSeconds()
      // 生成二进制流
      FileSaver.saveAs(content, 'tts-' + time + '.zip')
    })
  })
}

const getUrlBuffer = (url) => {
  return new Promise((resolve, reject) => {
    //通过请求获取文件blob格式
    let xmlhttp = new XMLHttpRequest()
    xmlhttp.open('GET', url, true)
    xmlhttp.responseType = 'blob'
    xmlhttp.onload = function () {
      if (this.status == 200) {
        resolve(this.response)
      } else {
        reject(this.status)
      }
    }
    xmlhttp.send()
  })
}

onMounted(() => {
  // 获取语音列表和区域列表
  axios
    .all([axios.get(baseUrl + '/voice'), axios.get(baseUrl + '/locale')])
    .then(
      axios.spread((voiceListRes, localeDictRes) => {
        if (voiceListRes.status == 200) {
          form.voiceList = voiceListRes.data
          if (!form.tts.region) tmpVoiceOption.value = voiceListRes.data
          else changeRegion(form.tts.region)
        }
        if (localeDictRes.status == 200) {
          form.localeDict = localeDictRes.data
        }
      })
    )
    .catch((error) => {
      // 请求失败的处理逻辑...
      console.error('error...', error)
    })

  // 读取本地缓存配置
  form.tts = JSON.parse(localStorage.getItem('tts.conf') || '{}')
})
</script>

<template>
  <el-container class="bd" style="height: 100%">
    <el-header class="">
      <el-row justify="center" align="middle" style="height: 100%">
        <div>觉醒运动科技-TTS文本语音转换工具</div>
      </el-row>
    </el-header>

    <el-container class="bd h-100">
      <el-main class="bd h-100">
        <el-container class="bd h-100" style="height: 100%">
          <el-aside
            width="300"
            style="display: flex; flex-direction: column; justify-content: space-between"
          >
            <el-table :data="mp3ResultList" style="width: 100%" min-height="100" max-height="300">
              <el-table-column
                prop="id"
                label="序号"
                width="60"
                header-align="center"
                align="center"
              />
              <el-table-column
                prop="text"
                label="文本"
                width="100"
                header-align="center"
                align="center"
              >
                <template #default="scope">
                  <el-tooltip
                    class="box-item"
                    effect="dark"
                    :content="scope.row.text"
                    placement="bottom"
                  >
                    {{
                      scope.row.text.length > 4
                        ? scope.row.text.substring(0, 4) + '...'
                        : scope.row.text
                    }}
                  </el-tooltip>
                </template>
              </el-table-column>
              <el-table-column
                fixed="right"
                label="操作"
                width="140"
                header-align="center"
                align="center"
              >
                <template #default="scope">
                  <el-button
                    link
                    type="primary"
                    size="small"
                    @click="handlePlay(scope.$index, scope.row)"
                  >
                    播放
                  </el-button>
                  <el-button
                    link
                    type="primary"
                    size="small"
                    @click="handleDownload(scope.$index, scope.row)"
                  >
                    下载
                  </el-button>
                  <el-button
                    link
                    type="primary"
                    size="small"
                    @click="handleDel(scope.$index, scope.row)"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
            <el-row justify="center" align="middle" style="margin: 10px">
              <el-link type="primary" @click="downloadAll"
                ><el-icon><Download /></el-icon>&nbsp;打包下载</el-link
              >
            </el-row>
          </el-aside>
          <el-main class="bd h-100" style="height: 100%">
            <el-input
              type="textarea"
              v-model="textInput"
              placeholder="请输入需要转换的文本内容"
              style="height: 100%; width: 100%"
              :autosize="{ minRows: 10 }"
            />
          </el-main>
        </el-container>
      </el-main>
      <el-aside
        class="bd"
        style="
          display: flex;
          flex-direction: row;
          justify-content: center;
          padding: 20px;
          max-width: 800px;
          min-width: 200px;
        "
      >
        <el-form :model="form" label-width="80px" label-position="left" style="width: 400px">
          <el-form-item label="语言/地区">
            <el-select
              v-model="form.tts.region"
              placeholder="请选择语言/地区"
              @change="changeRegion"
            >
              <el-option v-for="(item, key) in form.localeDict" :label="item" :value="key">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="音色">
            <el-select v-model="form.tts.voice" placeholder="请选择音色" @change="changeVoice">
              <el-option
                v-for="item in tmpVoiceOption"
                :label="item.shortName + '（' + item.sex + '）'"
                :value="item.name"
              ></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="格式">
            <el-select v-model="form.tts.fm" placeholder="请选择音频格式">
              <el-option
                v-for="item in form.formatList"
                :label="item.label"
                :value="item.value"
              ></el-option>
            </el-select>
          </el-form-item>
          <!-- <el-form-item label="风格">
            <el-select v-model="form.style" placeholder="请选择风格">
              <el-option label="Zone one" value="shanghai" />
              <el-option label="Zone two" value="beijing" />
            </el-select>
          </el-form-item>
          <el-form-item label="角色">
            <el-select v-model="form.role" placeholder="请选择角色">
              <el-option label="Zone one" value="shanghai" />
              <el-option label="Zone two" value="beijing" />
            </el-select>
          </el-form-item> -->
          <el-form-item label="语速">
            <el-slider v-model="form.tts.rate" :min="-50" :max="100" />
          </el-form-item>
          <el-form-item label="音调">
            <el-slider v-model="form.tts.volume" :min="-50" :max="100" />
          </el-form-item>
          <el-form-item label="频率">
            <el-slider v-model="form.tts.pitch" :min="-50" :max="200" />
          </el-form-item>
          <el-row style="justify-content: space-between">
            <el-button type="primary" @click="onSubmit" :loading="submitLoading"
              >开始转换</el-button
            >
            <el-button type="info" plain @click="onReset">恢复</el-button>
          </el-row>
        </el-form>
      </el-aside>
    </el-container>
    <el-footer class="">
      <el-row justify="center" align="middle" style="height: 100%">
        <div style="margin-right: 10px">
          <el-text class="mx-1" type="success" size="large">播放区</el-text>
        </div>
        <div>
          <audio controls ref="audioRef">
            <source type="audio/ogg" />
            <source type="audio/mpeg" />
            您的浏览器不支持 audio 元素。
          </audio>
        </div>
      </el-row>
    </el-footer>
  </el-container>
</template>

<style scoped>
.bd {
  border: 1px solid #eee;
}
</style>
